
package com.joysuch.wwyt.core.sqlparser.impl;

import java.util.ArrayList;
import java.util.List;

import com.joysuch.wwyt.core.sqlparser.SqlParserInterceptor;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.dataperm.context.DataPermContextHolder;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.update.Update;

public class TenantAndOrgCodeSqlParserInterceptor implements SqlParserInterceptor {

    @Override
    public void intercept(Statement parse) {
        if (parse instanceof Select) {
            long start = System.currentTimeMillis();
            if (((Select) parse).getSelectBody() instanceof PlainSelect) {
                parsePlainSelect(parse);
            } else if (((Select) parse).getSelectBody() instanceof SetOperationList) {
                SetOperationList list = (SetOperationList) ((Select) parse).getSelectBody();
            }
            long end = System.currentTimeMillis();
            // System.out.println(((end - start) + "ms ") + sql);
        } else if (parse instanceof Update) {

        } else if (parse instanceof Delete) {

        } else if (parse instanceof Insert) {
            // 检查是否包含TENENT_ID,ORG_CODE
            // 如果不包含，则添加。JPA不允许手写insert语句，只能通过实体添加，不需要考虑 insert ...select ...情况
            Insert insert = (Insert) parse;
            List<Column> columns = insert.getColumns();
            boolean containTenentId = false;
            boolean containOrgCode = false;
            for (Column c : columns) {
                if (c.getColumnName().equalsIgnoreCase("TENENT_ID")) {
                    containTenentId = true;
                }
                if (c.getColumnName().equalsIgnoreCase("ORG_CODE")) {
                    containOrgCode = true;
                }
            }
            if (!containTenentId) {
                columns.add(new Column("TENENT_ID"));
                insert.getColumns().add(new Column("TENENT_ID"));
                ((ExpressionList) insert.getItemsList()).getExpressions()
                        .add(new LongValue(Context.getCurrentTenantId()));
            }
            if (!containOrgCode) {
                columns.add(new Column("ORG_CODE"));
                insert.getColumns().add(new Column("ORG_CODE"));
                ((ExpressionList) insert.getItemsList()).getExpressions()
                        .add(new StringValue(Context.getCompanyCode()));
            }
        }
    }

    // 查询： 所有租户，组织Code共享的 实体列表： 菜单，数据字典 ,监测点类型
    private static List bypassTables = new ArrayList();
    static {

        bypassTables.add("WWYT_BASE_TYPE_GROUP");
        bypassTables.add("WWYT_BASE_TYPE_GROUP_ITEM");
        // 表名必须大写
        bypassTables.add("WWYT_API_DATA_ACCESS_URL");
        bypassTables.add("WWYT_BP_ENTERPRISE_INFO");
    }

    // 查询： 同一个租户内的不同公司， 共享同一个流程引擎配置
    private static List bypassOrgCodeTables = new ArrayList();
    static {
        // bypassTables.add("WWYT_WORKFLOW_PROCESS");
        // bypassTables.add("WWYT_WORKFLOW_MENU");
        // bypassTables.add("WWYT_WORKFLOW_OPTION");
        // bypassTables.add("WWYT_WORKFLOW_OPTION_TYPE");

    }

    /**
     * @param parse
     * @return
     */
    protected void parsePlainSelect(Statement parse) {
        PlainSelect plainSelect = (PlainSelect) ((Select) parse).getSelectBody();

        // 原where表达式
        Expression where = plainSelect.getWhere();
        List<Join> joins = plainSelect.getJoins();

        // 检查查询条件时候bypass 租户，组织条件
        if (checkBypassTable(plainSelect)) {
            return;
        }
        if(DataPermContextHolder.isTenantAndOrgCodeJoinSwitchFlag() && joins!= null && joins.size() > 0) {
            // 对开启join过滤标识的sql语句进行处理。理论上应该全部替换，担心对原有业务产生影响，先添加标识，只对启用的业务有效，降低影响
            where = createTwoTableJoinCondition(where, plainSelect.getFromItem().getAlias(), joins.get(0).getRightItem().getAlias(), joins.get(0));
            for(int i=1;i<joins.size();i++) {
                where = createSingleTableJoinCondition(where, joins.get(i).getRightItem().getAlias(), joins.get(i));
            }

        } else {
            Expression condition = createExpressionForTable(plainSelect.getFromItem().getAlias());
            if (joins != null) {
                for (Join j : joins) {
                    condition = new AndExpression(condition, createExpressionForTable(j.getRightItem().getAlias()));
                }
            }
            if (where == null) {
                where = condition;
            } else {
                where = new AndExpression(condition, where);
            }
        }

        // 设置新的where条件
        plainSelect.setWhere(where);
    }

    private Expression createSingleTableJoinCondition(Expression where, Alias table, Join join) {
        if(join.isLeft()) {
            // 左链接， 的右边表
            return and(where, createExpressionForTableWithOrNull(table));
        } else if(join.isRight()) {
            return and(where, createExpressionForTable(table));
        } else if(join.isInner()) {
            return and(where, createExpressionForTable(table));

        } else if(join.isFull()) {
            return and(where, createExpressionForTableWithOrNull(table));
        } else if(join.isCross()) {
            return and(where, createExpressionForTable(table));
        }
        return where;
    }

    private Expression createTwoTableJoinCondition(Expression where, Alias leftTable, Alias rightTable, Join join) {
        Expression exp = null;
        if(join.isLeft()) {
            // 左链接， 右边表可能为空
            exp = new AndExpression(createExpressionForTable(leftTable), createExpressionForTableWithOrNull(rightTable));
        } else if(join.isRight()) {
            exp = new AndExpression(createExpressionForTableWithOrNull(leftTable), createExpressionForTable(rightTable));
        } else if(join.isInner()) {
            exp = new AndExpression(createExpressionForTable(leftTable), createExpressionForTable(rightTable));
        } else if(join.isFull()) {
            exp = new AndExpression(createExpressionForTableWithOrNull(leftTable), createExpressionForTableWithOrNull(rightTable));
        } else if(join.isCross()) {
            exp = new AndExpression(createExpressionForTable(leftTable), createExpressionForTable(rightTable));
        }
        return and(where, exp);
    }

    private Expression and(Expression left, Expression right) {
        if(left != null && right != null) {
            return new AndExpression(right, left);
        } else if(right != null) {
            return right;
        } else {
            return left;
        }
    }

    private Expression createExpressionForTable(Alias alias) {
        String tenantColumn = "TENENT_ID";
        String orgCodeColumn = "ORG_CODE";
        if (alias != null) {
            tenantColumn = alias.getName() + "." + tenantColumn;
            orgCodeColumn = alias.getName() + "." + orgCodeColumn;
        }
        EqualsTo tenantId = new EqualsTo();
        tenantId.setLeftExpression(new Column(tenantColumn));
        tenantId.setRightExpression(new LongValue(Context.getCurrentTenantId()));

        LikeExpression code = new LikeExpression();
        code.setLeftExpression(new Column(orgCodeColumn));
        code.setRightExpression(new StringValue(Context.getCompanyCode() + "%"));
        return new AndExpression(tenantId, code);
    }

    private Expression createExpressionForTableWithOrNull(Alias alias) {
        String tenantColumn = "TENENT_ID";
        String orgCodeColumn = "ORG_CODE";
        if (alias != null) {
            tenantColumn = alias.getName() + "." + tenantColumn;
            orgCodeColumn = alias.getName() + "." + orgCodeColumn;
        }
        Column tc = new Column(tenantColumn);
        EqualsTo tenantId = new EqualsTo();
        tenantId.setLeftExpression(tc);
        tenantId.setRightExpression(new LongValue(Context.getCurrentTenantId()));

        Expression tenant = new Parenthesis(new OrExpression(tenantId, new IsNullExpression().withLeftExpression(tc)));

        Column oc = new Column(orgCodeColumn);
        LikeExpression code = new LikeExpression();
        code.setLeftExpression(oc);
        code.setRightExpression(new StringValue(Context.getCompanyCode() + "%"));

        Expression orgcode = new Parenthesis(new OrExpression(code, new IsNullExpression().withLeftExpression(oc)));

        return new Parenthesis(new AndExpression(tenant, orgcode));
    }

    /**
     * 检查当前表是否 在查询不添加 tenentId, orgCode 在 where 条件中
     *
     * @param plainSelect
     * @return
     */

    private boolean checkBypassTable(PlainSelect plainSelect) {
        FromItem fromItem = plainSelect.getFromItem();
        if (fromItem instanceof Table) {
            Table fromTable = (Table) fromItem;
            if (bypassTables.contains(fromTable.getFullyQualifiedName().toUpperCase())
                    || bypassOrgCodeTables.contains(fromTable.getFullyQualifiedName().toUpperCase())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean support() {
        return Context.getCurrentTenantId() != null;
    }
}
